home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / PCGPEV10.ZIP / BRES.TXT < prev    next >
Text File  |  1994-05-10  |  13KB  |  400 lines

  1.  
  2.                ┌────────────────────────────────────────┐
  3.                │ Bresenham's Line and Circle Algorithms │
  4.                └────────────────────────────────────────┘
  5.  
  6.                  Written for the PC-GPE by Mark Feldman
  7.             e-mail address : u914097@student.canberra.edu.au
  8.                              myndale@cairo.anu.edu.au
  9.  
  10.              ┌───────────────────────────────────────────┐
  11.              │      THIS FILE MAY NOT BE DISTRIBUTED     │
  12.              │ SEPARATE TO THE ENTIRE PC-GPE COLLECTION. │
  13.              └───────────────────────────────────────────┘
  14.  
  15.  
  16. ┌────────────┬───────────────────────────────────────────────────────────────
  17. │ Disclaimer │
  18. └────────────┘
  19.  
  20. I assume no responsibility whatsoever for any effect that this file, the
  21. information contained therein or the use thereof has on you, your sanity,
  22. computer, spouse, children, pets or anything else related to you or your
  23. existance. No warranty is provided nor implied with this information.
  24.  
  25. ┌──────────────┬───────────────────────────────────────────────────────────
  26. │ Introduction │
  27. └──────────────┘
  28.  
  29. Bresenham is a pretty smart cookie (note the use of the word "is", last I
  30. heard he was still working for IBM). This file contains the algorithms he
  31. developped for drawing lines and circles on a pixelated display system
  32. such as the VGA.
  33.  
  34. ┌────────────────┬─────────────────────────────────────────────────────────
  35. │ Line Algorithm │
  36. └────────────────┘
  37.  
  38. The basic algorithm works for lines which look like this:
  39.  
  40.  
  41.       o-------                         ┐
  42.      p1       --------                 │ deltay
  43.                       -------      p2  │
  44.                              -------o  ┘
  45.  
  46.       └────────────────────────────┘
  47.                   deltax
  48.  
  49. where p1 = (x1,y1),
  50.       p2 = (x2, y2),
  51.       x and y are both increasing from p1 to p2,
  52.       deltax = x2 - x1,
  53.       deltay = y2 - y1 and
  54.       deltax >= deltay.
  55.  
  56. All other types of lines can be derived from this type. I'll get to this
  57. bit later.
  58.  
  59. First you need to perform the following intialisation:
  60.  
  61. x = x1
  62. y = y1
  63. d = (2 * deltay) - deltax
  64.  
  65. x is the current x location, you will add 1 to this variable after every
  66. pixel you draw until all pixels have been drawn.
  67. y is the current y location. The decision variable is used to determine
  68. when to add 1 to this value. d is the decision variable which will be used
  69. to keep a track of what to do.
  70.  
  71. Now you loop across the screen from x1 to x2 and for each loop perform the
  72. following operations for each pixel :
  73.  
  74. PutPixel(x, y);  { Draw a pixel at the current point }
  75. if d < 0 then
  76.     d := d + (2 * deltay)
  77. else
  78.   begin
  79.     d := d + 2 * (deltay - deltax);
  80.     y := y + 1;
  81.   end;
  82. x := x + 1;
  83.  
  84. It's that simple!
  85.  
  86. ┌────────────────────────────────┬─────────────────────────────────────────
  87. │ Speeding Up The Line Algorithm │
  88. └────────────────────────────────┘
  89.  
  90. There are several useful techniques for speeding up Bresenhams line
  91. algorithm.
  92.  
  93. For starters, notice that all multiplications are by 2. This can be
  94. performed with a simple shift left instruction (Shl in Pascal, << in C).
  95.  
  96. Next notice that the values you add to the decision variable do not change
  97. throughout the loop, so they can be precalculated beforehand.
  98.  
  99. One property of lines is that they are symetrical about their mid-points,
  100. and we can use this property to speed up the algorithm. Store two x and y
  101. values, (xa, ya) and (xb, yb). Have each pair start on either end of the
  102. line. For each pass through the loop you draw the pixel at both points, add
  103. 1 to xa and subtract one from xb. When d >= 0 add 1 to ya and subtract one
  104. from yb. You then only need to loop until xa = xb.
  105.  
  106. It's also obvious that if the decision variable becomes the same value
  107. it was when it was initialised, then the rest of the line is just
  108. copies of the line you have already drawn up to that point. You might be
  109. able to speed the algorithm up by keeping an array of how y has been
  110. modified and then use this array if the line starts repeating itself. If
  111. you are using the Intel registers to store all values then you probably
  112. wouldn't get much of a speed increase (in fact it could slow it down), but
  113. it would probably be useful for thing like linear texture mapping (discussed
  114. below). I've never actually tried implementing this technique, and I would
  115. like to hear the results if anyone does.
  116.  
  117. Above all remember that these optimisations will only significantly speed
  118. up the line drawing algorithm if the whole thing is done in assembly. A
  119. profile of the example program at the end of this file showed that 40% of
  120. CPU time was spent in the slow PutPixel routine I was using, the loop
  121. mechanics and testing the sign of the decision variable.
  122.  
  123. ┌───────────────────────────────────┬──────────────────────────────────────
  124. │ Other Uses for the Line Algorithm │
  125. └───────────────────────────────────┘
  126.  
  127. A line can be represented by the equation y = mx + c, where
  128. m = deltay / deltax. Note that this is a version of the standard linear
  129. equation ax + bx + c = 0. There are many algorithms which use this equation.
  130.  
  131. One good use for the bresenham line algorithm is for quickly drawing filled
  132. concave polygons (eg triangles). You can set up an array of minimum and
  133. maximum x values for every horizontal line on the screen. You then use
  134. bresenham's algorithm to loop along each of the polygon's sides, find where
  135. it's x value is on every line and adjust the min and max values accordingly.
  136. When you've done it for every line you simply loop down the screen drawing
  137. horizontal lines between the min and max values for each line.
  138.  
  139. Another area is in linear texture mapping (see the PC-GPE article on texture
  140. mapping). This method involves taking a string of bitmap pixels and
  141. stretching them out (or squashing them in) to a line of pixels on the screen.
  142. Typically you would draw a vertical line down the screen and use Bresenhams
  143. to calculate which bitmap pixel should be drawn at each screen pixel.
  144.  
  145. ┌──────────────────┬───────────────────────────────────────────────────────
  146. │ Circle Algorithm │
  147. └──────────────────┘
  148.  
  149. Circles have the property of being highly symetrical, which is handy
  150. when it comes to drawing them on a display screen.
  151.  
  152.       |y          (This diagram is supposed to be a circle, try viewing
  153.       |           it in 50 line mode).
  154.   \ ..... /
  155.    .  |  .        We know that there are 360 degrees in a circle. First we
  156.   . \ | / .       see that a circle is symetrical about the x axis, so
  157.   .  \|/  .       only the first 180 degrees need to be calculated. Next
  158. --.---+---.--     we see that it's also symetrical about the y axis, so now
  159.   .  /|\  . x     we only need to calculate the first 90 degrees. Finally
  160.   . / | \ .       we see that the circle is also symetrical about the 45
  161.    .  |  .        degree diagonal axis, so we only need to calculate the
  162.   / ..... \       first 45 degrees.
  163.       |
  164.       |
  165.  
  166. Bresenhams circle algorithm calculates the locations of the pixels in the
  167. first 45 degrees. It assumes that the circle is centered on the origin. So
  168. for every pixel (x,y) it calculates we draw a pixel in each of the 8 octants
  169. of the circle :
  170.  
  171. PutPixel(CenterX + X, Center Y + Y)
  172. PutPixel(CenterX + X, Center Y - Y)
  173. PutPixel(CenterX - X, Center Y + Y)
  174. PutPixel(CenterX - X, Center Y - Y)
  175. PutPixel(CenterX + Y, Center Y + X)
  176. PutPixel(CenterX + Y, Center Y - X)
  177. PutPixel(CenterX - Y, Center Y + X)
  178. PutPixel(CenterX - Y, Center Y - X)
  179.  
  180. So let's get into the actual algorithm. Given a radius for the circle
  181. we perform this initialisation:
  182.  
  183. d := 3 - (2 * RADIUS)
  184. x := 0
  185. y := RADIUS
  186.  
  187. Now for each pixel we do the following operations:
  188.  
  189. Draw the 8 circle pixels
  190. if d < 0 then
  191.     d := d + (4 * x) + 6
  192. else
  193.   begin
  194.     d := d + 4 * (x - y) + 10
  195.     y := y - 1;
  196.   end;
  197.  
  198. And we keep doing this until x = y. Note that the values added to the
  199. decision variable in this algorithm (x and y) are constantly changing, so
  200. we cannot precalculate them. The muliplications however are by 4, and we
  201. can accomplish this by shifting left twice.
  202.  
  203.  
  204. ┌─────────────────────────────────┬─────────────────────────────────────────
  205. │ A Pascal General Line Procedure │
  206. └─────────────────────────────────┘
  207.  
  208. The basic bresenham line algorithm can be modified to handle all types of
  209. lines. In this section assume that deltax = abs(x2 - x1) and
  210. deltay = abs(y2 - y1).
  211.  
  212. First let's take lines where deltax >= deltay. Now if x1 > x2 then you will
  213. need to subtract 1 from x for every pass through the loop. Similarly if y1 >
  214. y2 then you will be also need to subtract 1 from y for every pass through the
  215. loop where d < 0.
  216.  
  217. Lines where deltax < deltay can be handled the same way, you just swap all
  218. the deltax's and deltay's around.
  219.  
  220. The fastest method of handling all cases is to write a custom routine for
  221. each of the 8 line types:
  222.  
  223. 1) x1 <= x2, y1 <= y2, deltax >= deltay
  224. 2) x1 <= x2, y1 <= y2, deltax <  deltay
  225. 3) x1 <= x2, y1 >  y2, deltax >= deltay
  226. 4) x1 <= x2, y1 >  y2, deltax <  deltay
  227. 5) x1 >  x2, y1 <= y2, deltax >= deltay
  228. 6) x1 >  x2, y1 <= y2, deltax <  deltay
  229. 7) x1 >  x2, y1 >  y2, deltax >= deltay
  230. 8) x1 >  x2, y1 >  y2, deltax <  deltay
  231.  
  232. This will give you the fastest results, but will also make your code 8
  233. times larger! Alternatively you can declare a few extra variables and
  234. use a common inner loop for all lines:
  235.  
  236. numpixels = number of pixels to draw
  237.           = deltax if deltax >= deltay or
  238.           = deltay if deltax < deltay
  239. dinc1 = the amount to add to d when d < 0
  240. dinc2 = the amount to add to d when d >= 0
  241. xinc1 = the amount to add to x when d < 0
  242. xinc2 = the amount to add to x when d >= 0
  243. yinc1 = the amount to add to y when d < 0
  244. yinc2 = the amount to add to y when d >= 0
  245.  
  246. The following is a simple example program which uses this technique:
  247.  
  248.  
  249. ────────────────────────────────────────────────────────────────────────
  250.  
  251. {
  252.  
  253. BRESLINE.PAS - A general line drawing procedure.
  254.                By Mark Feldman
  255.  
  256. This is a very simple implementation of bresenhams' line algorithm with
  257. no optimisations. It can draw about 6000 random lines a second in mode 13h
  258. on my 486SX33 with sloooooow Paradise Extended VGA.
  259.  
  260. }
  261.  
  262. procedure Line(x1, y1, x2, y2 : integer; color : byte);
  263. var i, deltax, deltay, numpixels,
  264.     d, dinc1, dinc2,
  265.     x, xinc1, xinc2,
  266.     y, yinc1, yinc2 : integer;
  267. begin
  268.  
  269.   { Calculate deltax and deltay for initialisation }
  270.   deltax := abs(x2 - x1);
  271.   deltay := abs(y2 - y1);
  272.  
  273.   { Initialize all vars based on which is the independent variable }
  274.   if deltax >= deltay then
  275.     begin
  276.  
  277.       { x is independent variable }
  278.       numpixels := deltax + 1;
  279.       d := (2 * deltay) - deltax;
  280.       dinc1 := deltay Shl 1;
  281.       dinc2 := (deltay - deltax) shl 1;
  282.       xinc1 := 1;
  283.       xinc2 := 1;
  284.       yinc1 := 0;
  285.       yinc2 := 1;
  286.     end
  287.   else
  288.     begin
  289.  
  290.       { y is independent variable }
  291.       numpixels := deltay + 1;
  292.       d := (2 * deltax) - deltay;
  293.       dinc1 := deltax Shl 1;
  294.       dinc2 := (deltax - deltay) shl 1;
  295.       xinc1 := 0;
  296.       xinc2 := 1;
  297.       yinc1 := 1;
  298.       yinc2 := 1;
  299.     end;
  300.  
  301.   { Make sure x and y move in the right directions }
  302.   if x1 > x2 then
  303.     begin
  304.       xinc1 := - xinc1;
  305.       xinc2 := - xinc2;
  306.     end;
  307.   if y1 > y2 then
  308.     begin
  309.       yinc1 := - yinc1;
  310.       yinc2 := - yinc2;
  311.     end;
  312.  
  313.   { Start drawing at <x1, y1> }
  314.   x := x1;
  315.   y := y1;
  316.  
  317.   { Draw the pixels }
  318.   for i := 1 to numpixels do
  319.     begin
  320.       PutPixel(x, y, color);
  321.       if d < 0 then
  322.         begin
  323.           d := d + dinc1;
  324.           x := x + xinc1;
  325.           y := y + yinc1;
  326.         end
  327.       else
  328.         begin
  329.           d := d + dinc2;
  330.           x := x + xinc2;
  331.           y := y + yinc2;
  332.         end;
  333.     end;
  334. end;
  335.  
  336. ────────────────────────────────────────────────────────────────────────
  337.  
  338.  
  339.  
  340.  
  341.  
  342.  
  343. Note that if you are writing a line routine for mode 13h (for example) you
  344. can speed it up by converting the inner loop to assembly and including
  345. mode 13h specific code. This portion of the above routine works the same but
  346. the <x, y> values are stored in a single variable (screen) which holds the
  347. memory address of the current pixel, screeninc1 and screeninc2 are the
  348. update values for screen.
  349.  
  350. ────────────────────────────────────────────────────────────────────────
  351.  
  352. var screen : word;
  353.     screeninc1, screeninc2 : integer;
  354.      .
  355.      .
  356.      .
  357.   { Start drawing at <x1, y1> }
  358.   screen := word(y1) * 320 + x1;
  359.   screeninc1 := yinc1 * 320 + xinc1;
  360.   screeninc2 := yinc2 * 320 + xinc2;
  361.  
  362.   { Draw the pixels }
  363.   asm
  364.  
  365.     { Use as many registers as are available }
  366.     push $A000
  367.     pop es
  368.     mov di, screen
  369.     mov dx, d
  370.     mov al, color
  371.     mov cx, numpixels
  372.     mov bx, dinc1
  373.  
  374.     @bres1:
  375.  
  376.     { Draw the current pixel and compare the decision variable to 0 }
  377.     mov es:[di], al
  378.     cmp dx, 0
  379.     jnl @bres2
  380.  
  381.     { D < 0 }
  382.     add dx, bx { bx = dinc1 }
  383.     add di, screeninc1
  384.     jmp @bres3
  385.  
  386.     @bres2:
  387.  
  388.     { D >= 0 }
  389.     add dx, dinc2
  390.     add di, screeninc2
  391.  
  392.     @bres3:
  393.  
  394.     loop @bres1
  395.   end;
  396.  
  397. ────────────────────────────────────────────────────────────────────────
  398.  
  399.  
  400.